package com.fourmob.datetimepicker.date;

import com.fourmob.datetimepicker.util.RecordDateUtil;

import ohos.agp.components.Component;
import ohos.agp.components.ListContainer;
import ohos.agp.components.Picker;
import ohos.app.Context;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;

public class DayPickerView extends ListContainer implements ListContainer.ScrolledListener,
    DatePickerDialog.OnDateChangedListener, Component.EstimateSizeListener {
    public static final int LIST_TOP_OFFSET = -1;
    protected static final int GOTO_SCROLL_DURATION = 250;
    protected static final int SCROLL_CHANGE_DELAY = 40;
    private static final String KEY_SELECTED_YEAR = "year";
    private static final String KEY_SELECTED_MONTH = "month";
    private static final String KEY_SELECTED_DAY = "day";

    private static HiLogLabel label = new HiLogLabel(HiLog.LOG_APP, 0x000110, "DatePickerDialog1");
    private static int choiceDay;
    private static int choiceMonth;

    private static int choiceYear = 1099;
    protected Context mContext;
    protected int mCurrentMonthDisplayed;
    protected EventHandler mHandler = new EventHandler(EventRunner.getMainEventRunner());
    protected SimpleMonthAdapter mAdapter;

    protected long mPreviousScrollPosition;
    protected int mCurrentScrollState = 0;
    protected int mPreviousScrollState = 0;
    protected ScrollStateRunnable mScrollStateChangedRunnable = new ScrollStateRunnable();
    protected SimpleMonthAdapter.CalendarDay mSelectedDay = new SimpleMonthAdapter.CalendarDay();
    protected SimpleMonthAdapter.CalendarDay mTempDay = new SimpleMonthAdapter.CalendarDay();

    protected int mNumWeeks = 6;
    protected boolean mShowWeekNumber = false;
    protected int mDaysPerWeek = 7;
    protected float mFriction = 1.0F;
    private DatePickerController mController;
    private boolean mPerformingScroll;
    private int firstLoad = 0; // 0代表 是第一次加载,其余数字代表不是

 /**
     * 日历picker构造方法
     * @param context 上下文
     * @param datePickerController 控制器
 ***/
	public DayPickerView(Context context, DatePickerController datePickerController) {
		super(context);
        HiLog.info(label, "DPV_DayPickerView");
		mController = datePickerController;
		mController.registerOnDateChangedListener(this);
        HiLog.info(label, "DPV_DayPickerView00");
        HiLog.info(label,"PickerView_Construct " + mController.getSelectedDay().year + " "
            + mController.getSelectedDay().month + " " + mController.getSelectedDay().day);
	    setLayoutConfig(new ListContainer.LayoutConfig(LayoutConfig.MATCH_PARENT,
                LayoutConfig.MATCH_PARENT));
        setEstimateSizeListener(this);
	    init(context);
	    RecordDateUtil.setIsFirstChange(true);

        HiLog.info(label," DPV_Controller " + RecordDateUtil.isIsFirstChange());
        HiLog.info(label, "DPV_DayPickerView01");
	}

	public int getMostVisiblePosition() {
        HiLog.info(label, "DPV_getMostVisiblePostion00");
        final int firstPosition = getFirstVisibleItemPosition();
        final int height = getHeight();

        int maxDisplayedHeight = 0;
        int mostVisibleIndex = 0;
        int i = 0;
        int bottom = 0;
        HiLog.info(label, "DPV_getMostVisiblePostion01");
        while (bottom < height) {
            Component child = getComponentAt(i);
            HiLog.info(label, "DPV_getMostVisiblePostion01a");
            if (child == null) {
                break;
            }
            HiLog.info(label, "DPV_getMostVisiblePostion01b");
            bottom = child.getBottom();
            int displayedHeight = Math.min(bottom, height) - Math.max(0, child.getTop());
            if (displayedHeight > maxDisplayedHeight) {
                mostVisibleIndex = i;
                maxDisplayedHeight = displayedHeight;
            }
            i++;
        }
        HiLog.info(label, "DPV_getMostVisiblePostion02");
        return firstPosition + mostVisibleIndex;
	}

	public boolean goTo(SimpleMonthAdapter.CalendarDay day, boolean animate, boolean setSelected,
                        boolean forceScroll) {
        HiLog.info(label, "DPV_goto " + day.year + " : " + day.month + " : " + day.day + " " + setSelected);
        if (setSelected) {
            HiLog.info(label, "DPV_goto00 " + (mSelectedDay == null) + ""
                + " Selected " + mSelectedDay.year + " " + mSelectedDay.month + " " + mSelectedDay.day);
            mSelectedDay.day = day.day;
            mSelectedDay.month = day.month;
            mSelectedDay.year = day.year;
            mSelectedDay.set(day);
        }
        mTempDay.set(day);
        HiLog.info(label, "DPV_gotoXX02");
        final int position = (day.year - mController.getMinYear())
                * SimpleMonthAdapter.MONTHS_IN_YEAR + day.month;
        Component child;
        int i = 0;
        int top = 0;
        do {
            child = getComponentAt(i++);
            HiLog.info(label, "DPV_goto0b " + (child == null));
            if (child == null) {
                break;
            }
            HiLog.info(label, "DPV_goto01");
            top = child.getTop();
        } while (top < 0);
        // Compute the first and last position visible
        int selectedPosition;
        if (child != null) {
            selectedPosition = getIndexForComponent(child);
            HiLog.info(label, "DPV_goto02");
        } else {
            selectedPosition = 0;
        }
        HiLog.info(label, "DPV_goto02xx " + mSelectedDay.year + " " + mSelectedDay.month + " " + mSelectedDay.day);
        if (setSelected) {
            mAdapter.setSelectedDay(mSelectedDay);
        }
        mAdapter.setSelectedDay(mSelectedDay);
        HiLog.info(label, "DPV_goto03");
        if (position != selectedPosition || forceScroll) {
            setMonthDisplayed(mTempDay);
            HiLog.info(label, "DPV_goto04 " + position);
              mPreviousScrollState = Picker.ScrolledListener.STATE_FLING;
            //  以下方法用于替换,不完全符合相关要求,但没有合适的api
            scrollToCenter(position);
            scrollTo(position);
            HiLog.info(label, "DPV_goto05");
        } else if (setSelected) {
            setMonthDisplayed(mSelectedDay);
            HiLog.info(label, "DPV_goto06");
        }
        return false;
	}

	public void init(Context paramContext) {
        HiLog.info(label, "DPV_init");
		setUpListView();
		setUpAdapter();
		setItemProvider(mAdapter);
        HiLog.info(label, "DPV_init01");
	}

	protected void layoutChildren() {
        HiLog.info(label, "DPV_layoutChildren");
		if (mPerformingScroll) {
			mPerformingScroll = false;
            HiLog.info(label, "DPV_layoutChildren00");
		}
	}

	public void onChange() {
        HiLog.info(label, "DPV_onChange");
		setUpAdapter();
		setItemProvider(mAdapter);
        HiLog.info(label, "DPV_onChange00");
	}

	/**
     * 设置选择的天
     * @param choiceDay
     */
    public static void setChoiceDay(int choiceDay) {
        DayPickerView.choiceDay = choiceDay;

    }

    /**
     * 设置选择的月份
     * @param choiceMonth
     */
    public static void setChoiceMonth(int choiceMonth) {
        DayPickerView.choiceMonth = choiceMonth;
    }

    /**
     * 设置月份
     * @param choceMonth
     */
    public void setChoceMonth(int choceMonth) {
        setChoiceMonth(choceMonth);
    }

    /**
     * 设置天
     * @param choceDay
     */
    public void setChoceDay(int choceDay) {
        setChoiceDay(choceDay);
    }

    /**
     * 设置选择的年份
     * @param choiceYear
     * **/
    public static void setChoiceYear(int choiceYear) {
        DayPickerView.choiceYear = choiceYear;
    }

    /**
     * 设置年
     * @param choceYear
     **/
    public void setChoceYear(int choceYear) {
        setChoiceYear(choceYear);
    }

    public void onDateChanged() {
        HiLog.info(label, "DPV_onDateChanged first " + firstLoad+" first " + RecordDateUtil.isIsFirstChange());
        int year = RecordDateUtil.getYear();
        setChoceYear(year);
        setChoceMonth(RecordDateUtil.getMonth());
        setChoceMonth(RecordDateUtil.getDay());
        if(choiceYear != 1099) {
            SimpleMonthAdapter.CalendarDay calendarDay = new
                SimpleMonthAdapter.CalendarDay(choiceYear,choiceMonth,choiceDay);
            goTo(calendarDay, false, true, true);
        } else {
            goTo(mSelectedDay,false,true,true);
        }
        RecordDateUtil.setIsFirstChange(false);
        HiLog.info(label, "DPV_onDateChanged00");
	}

	public void onScroll(ListContainer view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        HiLog.info(label, "DPV_onScroll");
        SimpleMonthView child = (SimpleMonthView) view.getComponentAt(0);
        if (child == null) {
            return;
        }
        HiLog.info(label, "DPV_onScroll00");

        // Figure out where we are
        long currScroll = view.getFirstVisibleItemPosition() * child.getHeight() - child.getBottom();
        mPreviousScrollPosition = currScroll;
        mPreviousScrollState = mCurrentScrollState;
        HiLog.info(label, "DPV_onScroll01");
	}

	public void onScrollStateChanged(ListContainer absListView, int scroll) {
        HiLog.info(label, "DPV_onScrollStateChanged " + scroll);
		mScrollStateChangedRunnable.doScrollStateChange(absListView, scroll);
        HiLog.info(label, "DPV_onScrollStateChanged00");
	}

	public void postSetSelection(final int position) {
        HiLog.info(label, "DPV_postSetSelection");
		clearFocus();
        HiLog.info(label, "DPV_postSetSelection00");
        mContext.getUITaskDispatcher().asyncDispatch(new Runnable(){
            @Override
            public void run() {
                HiLog.info(label, "DPV_postSetSelection01");
                scrollTo(position);
            }
        });
        HiLog.info(label, "DPV_postSetSelection02");
		onScrollStateChanged(this, 0);
        HiLog.info(label, "DPV_postSetSelection03");
	}

	protected void setMonthDisplayed(SimpleMonthAdapter.CalendarDay calendarDay) {
        HiLog.info(label, "DPV_setMonthDisplayed");
		this.mCurrentMonthDisplayed = calendarDay.month;
        invalidate();
        HiLog.info(label, "DPV_setMonthDisplayed00");
	}

	protected void setUpAdapter() {
        HiLog.info(label, "DPV_setUpAdapter");
        HiLog.info(label, "DPV_setUpAdapter00 " + mSelectedDay.year
            + ", " + mSelectedDay.month + "  " + mSelectedDay.day);
		if (mAdapter == null) {
            HiLog.info(label, "DPV_setUpAdapter0a");
			mAdapter = new SimpleMonthAdapter(getContext(), mController);
        }
        mAdapter.setSelectedDay(mController.getSelectedDay());
        HiLog.info(label, "DPV_setUpAdapter00 " + mSelectedDay.year
            + ", " + mSelectedDay.month + "  " + mSelectedDay.day);
        HiLog.info(label, "DPV_setUpAdapter01");
	}

	protected void setUpListView() {
        HiLog.info(label, "DPV_setUpListView_first");
        setVibrationEffectEnabled(false);
	    setScrolledListener(this);
        HiLog.info(label, "DPV_setUpListView");
	}

    @Override
    public void onContentScrolled(Component component, int i, int i1, int i2, int i3) {
        HiLog.info(label, "DPV_onContentScrolled");
        HiLog.info(label, "DPV_onScroll " + (component == null));
        long currScroll = i1 - i3;
        mPreviousScrollPosition = currScroll;
        mPreviousScrollState = mCurrentScrollState;
        HiLog.info(label, "DPV_onScroll01  " +i1 + "  " + i3);
    }

    private void onScrollChange() {
	    onDateChanged();
    }

    @Override
    public boolean onEstimateSize(int i, int i1) {
	    layoutChildren();
	    HiLog.info(label,"onEstimate1  " + mSelectedDay.year + ", month "
                + mSelectedDay.month + ", day " + mSelectedDay.day);
        HiLog.info(label,"onEstimate2  " + mController.getSelectedDay().year + ", month "
                + mController.getSelectedDay().month + ", day " + mController.getSelectedDay().day);
        HiLog.info(label," onEsi " + RecordDateUtil.isIsFirstChange());
	    if (RecordDateUtil.isIsFirstChange() == true) {
            onDateChanged();
            RecordDateUtil.setIsFirstChange(false);
        }
        return false;
    }

    protected class ScrollStateRunnable implements Runnable {
        private int mNewState;

        /**
         * Sets up the runnable with a short delay in case the scroll state
         * immediately changes again.
         *
         * @param view The list view that changed state
         * @param scrollState The new state it changed to
         */
        public void doScrollStateChange(ListContainer view, int scrollState) {
            HiLog.info(label, "DPV_doScrollStateChange");
            mHandler.removeTask(this::run);
            mNewState = scrollState;
            mHandler.postTimingTask(this::run,SCROLL_CHANGE_DELAY);
            HiLog.info(label, "DPV_doScrollStateChange00");
        }

        @Override
        public void run() {
            mCurrentScrollState = mNewState;

            // Fix the position after a scroll or a fling ends
            if (mNewState == Picker.ScrolledListener.STATE_HALT
                    && mPreviousScrollState != Picker.ScrolledListener.STATE_HALT
                    && mPreviousScrollState != Picker.ScrolledListener.STATE_DRAGGING) {
                mPreviousScrollState = mNewState;
                int i = 0;
                HiLog.info(label, "DPV_doScrollStateChange01");
                Component child = getComponentAt(i);
                while (child != null && child.getBottom() <= 0) {
                    child = getComponentAt(++i);
                }
                if (child == null) {
                    // The view is no longer visible, just return
                    return;
                }
                HiLog.info(label, "DPV_doScrollStateChange02");
                int firstPosition = getFirstVisibleItemPosition();
                int lastPosition = getLastVisibleItemPosition();
                boolean scroll = firstPosition != 0 && lastPosition != getChildCount() - 1;
                final int top = child.getTop();
                final int bottom = child.getBottom();
                final int midpoint = getHeight() / 2;
                if (scroll && top < LIST_TOP_OFFSET) {
                    if (bottom > midpoint) {
                        HiLog.info(label, "DPV_doScrollStateChange03");
                        scrollTo(top);
                    } else {
                        HiLog.info(label, "DPV_doScrollStateChange04");
                        scrollTo(bottom);
                    }
                }
            } else {
                mPreviousScrollState = mNewState;
            }
        }
    }

}